package com.brayan.webapp.repository.general.i18n;

import java.util.LinkedList;
import java.util.List;

import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Root;

import org.apache.log4j.Logger;

import com.brayan.webapp.model.general.country.Country;
import com.brayan.webapp.model.general.country.Country_;
import com.brayan.webapp.model.general.i18n.Language;
import com.brayan.webapp.model.general.i18n.Language_;
import com.brayan.webapp.model.general.i18n.Locale;
import com.brayan.webapp.model.general.i18n.Locale_;
import com.brayan.webapp.model.general.i18n.Text;
import com.brayan.webapp.model.general.i18n.Text_;
import com.brayan.webapp.model.general.i18n.Translation;
import com.brayan.webapp.model.general.i18n.Translation_;

@Stateless
public class TranslationRepository {
	
	@Inject
	transient private Logger LOGGER; 

	@PersistenceContext(unitName = "ccms")
	EntityManager em;
		
	
	public void saveTranslation(Translation trans){
		em.merge(trans);		
	}
	
	/**
	 * Get the translation of a text.
	 * 
	 * @param id
	 * @return
	 */
	public List<Translation> getTranslationsByTextId(Long id) {
		List<Translation> translations = new LinkedList<Translation>();
		CriteriaBuilder critBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Translation> criteriaQuery = critBuilder.createQuery(Translation.class);

		Root<Translation> translation = criteriaQuery.from(Translation.class);
		criteriaQuery.select(translation);
		criteriaQuery.where(critBuilder.equal(translation.get(Translation_.textUnit), id));

		TypedQuery<Translation> typedQuery = em.createQuery(criteriaQuery);
		translations = typedQuery.getResultList();
		return translations;
	}

	/**
	 * Use this query if your are absolutely sure that the combination of textId
	 * and localeId exists.
	 * 
	 * @param textId
	 * @param LocaleId
	 * @return
	 */
	public Translation getTranslationByTextIdAndLocaleId(Long textId, Long localeId) {

		CriteriaBuilder critBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Translation> criteriaQuery = critBuilder.createQuery(Translation.class);

		Root<Translation> rootTranslation = criteriaQuery.from(Translation.class);
		Join<Translation, Locale> locale = rootTranslation.join(Translation_.locale);
		Join<Translation, Text> textUnit = rootTranslation.join(Translation_.textUnit);

		criteriaQuery.select(rootTranslation);
		criteriaQuery.where(critBuilder.equal(textUnit.get(Text_.id), textId),
				critBuilder.equal(locale.get(Locale_.id), localeId));

		TypedQuery<Translation> typedQuery = em.createQuery(criteriaQuery);
		Translation translation = null;

		try {
			translation = typedQuery.getSingleResult();
		} catch (NoResultException nrEx) {
			LOGGER.warn("No translation foundy by text id (" + textId + "), locale id (" + localeId + ").");
		}

		return translation;
	}

	/**
	 * 
	 * Provide the text unit id, language and a country code to fetch a
	 * translation. Both, language and country MUST match to be returned!
	 * 
	 * @param text
	 *            id
	 * @param languageCode
	 * @param countryCode
	 * @return
	 */
	public Translation getTranslationByTextIdAndLocaleCode(Long textUnitId, String languageCode, String countryCode) {

		CriteriaBuilder critBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Translation> criteriaQuery = critBuilder.createQuery(Translation.class);

		Root<Translation> rootTranslation = criteriaQuery.from(Translation.class);
		Join<Translation, Text> textUnit = rootTranslation.join(Translation_.textUnit);
		Join<Locale, Country> country = rootTranslation.join(Translation_.locale).join(Locale_.country);
		Join<Locale, Language> language = rootTranslation.join(Translation_.locale).join(Locale_.language);

		criteriaQuery.select(rootTranslation);
		criteriaQuery.where(
				critBuilder.equal(textUnit.get(Text_.id), textUnitId),
				critBuilder.equal(country.get(Country_.countryCode), countryCode),
				critBuilder.equal(language.get(Language_.languageCode), languageCode));

		TypedQuery<Translation> typedQuery = em.createQuery(criteriaQuery);

		Translation translation = null;
		try {
			translation = typedQuery.getSingleResult();
		} catch (NoResultException nrEx) {
			LOGGER.info("No translation foundy by text id (" + textUnitId + "), language (" + languageCode
					+ ") and country code (" + countryCode + ").");
		}
		return translation;
	}

	/**
	 * TODO VERIFY THIS METHOD
	 * 
	 * Use this query to retrieve the translation ONLY by language code and by a
	 * full locale code.
	 * 
	 * 
	 * @param textUnitId
	 * @param languageCode
	 * @return
	 */
	public Translation getTranslationByTextIdAndLanguageCode(Long textUnitId, String languageCode) {
		CriteriaBuilder critBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Translation> criteriaQuery = critBuilder.createQuery(Translation.class);

		Root<Translation> rootTranslation = criteriaQuery.from(Translation.class);
		Join<Translation, Locale> locale = rootTranslation.join(Translation_.locale);
		Join<Translation, Text> textUnit = rootTranslation.join(Translation_.textUnit);
		Join<Locale, Language> language = rootTranslation.join(Translation_.locale).join(Locale_.language);
		

		criteriaQuery.select(rootTranslation);
		criteriaQuery.where(critBuilder.equal(textUnit.get(Text_.id), textUnitId),
				critBuilder.equal(language.get(Language_.languageCode), languageCode),
				critBuilder.isNull(locale.get(Locale_.country))
				);

		TypedQuery<Translation> typedQuery = em.createQuery(criteriaQuery);

		Translation translation = null;
		try {
			translation = typedQuery.getSingleResult();
		} catch (NoResultException nrEx) {
			LOGGER.info("No translation foundy by text id (" + textUnitId + "), language (" + languageCode + ").");
		}

		return translation;
	}

	public void deleteText(long textId) {
		Text text = em.find(Text.class, textId);
		if(text != null)
			em.remove(text);		
	}

	public Translation getTranslationByTokenAndLocaleId(String token, Locale localeObj) {
		CriteriaBuilder critBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Translation> criteriaQuery = critBuilder.createQuery(Translation.class);

		Root<Translation> rootTranslation = criteriaQuery.from(Translation.class);
		Join<Translation, Locale> locale = rootTranslation.join(Translation_.locale);
		Join<Translation, Text> textUnit = rootTranslation.join(Translation_.textUnit);

		criteriaQuery.select(rootTranslation);
		criteriaQuery.where(critBuilder.equal(textUnit.get(Text_.token), token),
				critBuilder.equal(locale.get(Locale_.id), localeObj.getId()));

		TypedQuery<Translation> typedQuery = em.createQuery(criteriaQuery);
		Translation translation = null;

		try {
			translation = typedQuery.getSingleResult();
		} catch (NoResultException nrEx) {
			LOGGER.info("No translation foundy by token (" + token + "), locale id (" + localeObj.getId() + ").");
		}

		return translation;
	}

}
